<template>
	<view class="calendar" :style="{ background: copyData.background }">
		<view id="header" class="header">
			<slot name="header"></slot>
			<view class="header_box">
				<view class="weeks">
					<view v-for="weekItem in week" :key="weekItem">
						{{ weekItem }}
					</view>
				</view>
			</view>
		</view>
		<scroll-view class="wrap" :scroll-y="true" :style="scrollHeight ? '' : 'height: auto '">
			<view class="item" v-for="item in dateArray" :key="item.cn">
				<view class="yearMonth" :style="{ fontSize: copyData.yearMonthSize, color: copyData.yearMonthColor }">
					{{ item.cn }}
				</view>
				<view class="day-wrap">
					<view
						class="day"
						v-for="dayItem in item.daysArray"
						@click="chooseDay(dayItem)"
						:style="{
							'min-height': copyData.dayHeight
						}"
						:key="dayItem.date">
						<view
							:class="['item', handleDayClass(dayItem)]"
							:style="{
								'min-height': copyData.dayHeight,
								'background-color': dayItem[activeKey] == '不可预订' ? 'red' : ''
							}">
							<!-- 天数本体 -->
							<view
								:style="{
									fontSize: copyData.daySize,
									color: dayItem[activeKey] == '不可预订' ? '#fff' : ''
								}">
								{{ dayItem.day }}
							</view>
							<view
								class="festival"
								:style="{
									fontSize: copyData.festivalSize,
									color: dayItem[activeKey] == '不可预订' ? '#fff' : copyData.festivalColor
								}">
								{{ dayItem.festival }}
							</view>
							<view
								class="activeKey"
								:style="{
									fontSize: copyData.activeKeySize,
									color: dayItem[activeKey] == '不可预订' ? '#fff' : ''
								}"
								v-if="activeKey && dayItem[activeKey]">
								{{ dayItem[activeKey] }}
							</view>

							<!-- 开始结束不是同一天 -->
							<view class="areaStatus" :style="{ fontSize: copyData.rangeSize }" v-if="!single">
								<!-- 条件： isTodaySE判断是不是当天进出 如果不是,即false，并且当前的时间戳===选择的第一个时间戳 selectOptionsArray[0] 显示入住-->
								<view
									class="start"
									v-if="
										!isTodaySE(dayItem) &&
										selectOptionsArray[0] &&
										selectOptionsArray[0].timeStamp === dayItem.timeStamp
									">
									{{ copyData.optionsBtn[0] }}
								</view>
								<!-- 条件： isTodaySE判断是不是当天进出 如果不是,即false，并且当前的时间戳===选择的第一个时间戳 selectOptionsArray[1] 显示离店-->
								<view
									class="end"
									v-if="
										!isTodaySE(dayItem) &&
										selectOptionsArray[1] &&
										selectOptionsArray[1].timeStamp === dayItem.timeStamp
									">
									{{ copyData.optionsBtn[1] }}
								</view>
							</view>
							<!-- 开始结束时当天才显示 -->
							<view class="end" :style="{ fontSize: copyData.rangeSize }" v-if="!single && isTodaySE(dayItem)">
								{{ copyData.optionsBtn[1] }}
							</view>
						</view>
					</view>
				</view>
			</view>
		</scroll-view>
		<view id="footer" class="footer">
			<slot name="footer">
				<view class="inbottom">
					<button
						@click="cancel()"
						class="cancel"
						:style="{
							background: copyData.cancelBtn.btnBackground,
							fontSize: copyData.cancelBtn.btnTitleSize,
							color: copyData.cancelBtn.btnTitleColor
						}">
						取消
					</button>
					<button
						@click="confirm()"
						class="confirm"
						:style="{
							background: copyData.confirmBtn.btnBackground,
							fontSize: copyData.confirmBtn.btnTitleSize,
							color: copyData.confirmBtn.btnTitleColor
						}">
						确定
					</button>
				</view>
			</slot>
		</view>
	</view>
</template>

<script>
	export default {
		name: 'calendar',
		props: {
			limit: {
				type: Number,
				default: 6
			},
			startDate: {
				type: [String, Number, null],
				default: null
			},
			// 新增单选模式属性
			single: {
				type: Boolean,
				default: false
			},
			// 修改range属性为可选
			range: {
				type: Object,
				default: () => ({})
			},
			maxRange: {
				type: Number,
				default: 30
			},
			activeKey: {
				type: String,
				default: 'price'
			},
			activeArray: {
				type: Array,
				default: () => []
			},
			opts: {
				type: Object,
				default: () => ({})
			}
		},
		watch: {
			activeArray: {
				handler(newVal) {
					// 当 activeArray 变化时，重新设置日期项的键值
					this.dateArray = this.setDayItemKey(this.dateArray)
				},
				deep: true // 深度监听，确保数组内部变化也能触发
			}
		},
		data() {
			return {
				dateArray: [],
				week: ['日', '一', '二', '三', '四', '五', '六'],
				selectOptionsArray: [],
				copyOpts: {
					popup: false,
					optionsBtn: ['入住', '离店'], //入住离店的文字，可以自己改
					dayHeight: '100rpx',
					scrollHeight: '',
					daySize: '22rpx',
					rangeSize: '18rpx',
					yearMonthSize: '28rpx',
					background: '#fff',
					yearMonthColor: '#333',
					activeKeySize: '20rpx',
					activeKeyColor: '#787878',
					festivalSize: '16rpx',
					festivalColor: '#2367FA',
					confirmBtn: {
						btnBackground: 'orange',
						btnTitleSize: '32rpx',
						btnTitleColor: '#fff'
					},
					cancelBtn: {
						btnBackground: '#fff',
						btnTitleSize: '32rpx',
						btnTitleColor: '#333'
					},
					festival: [
						// { date: '05-01', title: '劳动节' },
						// { date: '10-01', title: '国庆节' },
						// { date: '01-01', title: '元旦' },
						// { date: '06-01', title: '儿童节' },
						// { date: '05-04', title: '青年节' },
						// { date: '03-08', title: '妇女节' },
						// { date: '04-01', title: '愚人节' },
						// { date: '12-25', title: '圣诞节' },
						// { date: '07-01', title: '建党节' },
						// { date: '08-01', title: '建军节' }
					]
				},
				scrollHeight: '',
				posfixTop: '44px',
				containerHeight: '100%',
				startRangeTimeStamp: 0
			}
		},
		computed: {
			copyData() {
				const copy = Object.assign(this.copyOpts, this.opts)
				return copy
			},
			dayNums() {
				// 单选模式下始终返回1
				if (this.single) return 1

				let days = 0
				if (this.selectOptionsArray.length === 2) {
					const timeStamp = this.selectOptionsArray[1].timeStamp - this.selectOptionsArray[0].timeStamp
					const num = Math.floor(timeStamp / 86400000)
					if (num === 0) {
						days = 1
					} else {
						days = num
					}
				}
				return days
			}
		},
		mounted() {
			this.copyOpts = Object.assign(this.copyOpts, this.opts)
			this.startRangeTimeStamp = this.startDate
			this.init()
		},
		methods: {
			// 获取容器高度
			async getContainerHeight() {
				// #ifdef MP-WEIXIN
				const menuButton = uni.getMenuButtonBoundingClientRect()
				const pdt = menuButton.bottom + 4
				this.posfixTop = pdt + 'px'
				// #endif
				const systemInfo = uni.getSystemInfoSync()
				const { height } = systemInfo.safeArea
				let header = await this.getHeight('#header')
				let footer = await this.getHeight('#footer')
				let scrollHeight = null
				// #ifdef MP-WEIXIN
				scrollHeight = Math.floor(((height - header - footer - pdt - 44) / 3) * 2)
				// #endif
				// #ifndef MP-WEIXIN
				scrollHeight = Math.floor(((height - header - footer - 138) / 3) * 2)

				// #endif

				this.scrollHeight = scrollHeight + 'px'
				this.containerHeight = height - 138 + 'px'
			},
			// 需要用promise确保拿到高度数据
			getHeight(element) {
				return new Promise((resolve, reject) => {
					const query = uni.createSelectorQuery().in(this)
					query
						.select(element)
						.boundingClientRect((data) => {
							if (data) {
								const height = data.height
								resolve(height)
							}
						})
						.exec()
				})
			},
			// 初始化
			async init() {
				await this.handleStartDateArea()
				await this.handleInitRange()
				await this.createDaysArray()
				await this.getContainerHeight()
			},
			// 生成日期列表
			createDaysArray() {
				let { year, month, week } = this.handleDateData(this.startRangeTimeStamp)
				const limit = this.limit
				const dateArray = []
				const len = limit ? limit : 3
				month = month - 1
				for (let i = 0; i < len; i++) {
					let lastM = month
					month = month + 1
					if (month > 12) {
						month = 1
						year++
					}
					let nextM = month + 1 > 12 ? 1 : month + 1
					dateArray.push({
						year,
						lastM,
						month,
						nextM,
						daysArray: [],
						cn: `${year}年${month}月`,
						days: this.getDayNum(month, year)
					})
				}

				const newArray = dateArray.map((item) => {
					for (let i = 1; i <= item.days; i++) {
						item.daysArray.push(this.handleDateData(`${item.year}/${item.month}/${i}`))
					}
					item.daysArray.forEach((n) => {
						for (let j = item.daysArray[0].week; j > 0; j--) {
							item.daysArray.unshift({
								date: item.cn + 'key of ' + j
							})
						}
					})
					return item
				})
				this.dateArray = this.setDayItemKey(newArray)
			},
			// 传入了初始的选择日期
			handleInitRange() {
				if (this.single && this.range.hasOwnProperty('start')) {
					// 单选模式下只使用start
					this.selectOptionsArray = [this.handleDateData(this.range.start + ' 00:00')]
				} else if (this.range.hasOwnProperty('start') && this.range.hasOwnProperty('end')) {
					const { start, end } = this.range
					this.selectOptionsArray = [this.handleDateData(start + ' 00:00'), this.handleDateData(end + ' 00:00')]
				}
			},
			// 插入键值
			setDayItemKey(arr) {
				const copy = JSON.parse(JSON.stringify(arr))
				if (this.activeArray && this.activeArray.length) {
					copy.forEach((item) => {
						item.daysArray.forEach((day) => {
							// 只有当 day 有日期信息时才进行处理
							if (day.date) {
								for (let i in this.activeArray) {
									const activeItem = this.activeArray[i]
									if (day.date === activeItem.date || day.date === activeItem.dateIOS) {
										// 使用 Object.assign 合并属性
										Object.assign(day, activeItem)
									}
								}
							}
						})
					})
				}
				return copy
			},
			// 创建月份数组
			createDayArray(num) {
				const array = new Array()
				for (let i = 1; i <= num; i++) {
					array.push(i)
				}
				return array
			},
			// 每个月多少天
			getDayNum(month, year) {
				const largeMonth = [1, 3, 5, 7, 8, 10, 12]
				const smallMonth = [4, 6, 9, 11]
				if (month != 2 && largeMonth.includes(month)) {
					return 31
				}
				if (month != 2 && smallMonth.includes(month)) {
					return 30
				}
				if ((year % 100 != 0 && year % 4 === 0) || (year % 100 === 0 && year % 400 === 0)) {
					return 29
				} else {
					return 28
				}
			},
			// 每天应该有的数据
			handleDateData(time) {
				if (typeof time === 'String') {
					time = time.replace(/-/g, '/')
				}
				const weekCNArray = this.week
				const date = new Date(time)
				let year = date.getFullYear()
				let month = date.getMonth() + 1
				let week = date.getDay()
				let day = date.getDate()
				let hour = date.getHours()
				let min = date.getMinutes()
				let sec = date.getSeconds()
				let timeStamp = date.getTime()
				const weekCN = weekCNArray[week]
				month = month < 10 ? '0' + month : month
				day = day < 10 ? '0' + day : day
				hour = hour < 10 ? '0' + hour : hour
				min = min < 10 ? '0' + min : min
				sec = sec < 10 ? '0' + sec : sec

				const obj = {
					year: Number(year),
					month: Number(month),
					week,
					day: Number(day),
					timeStamp,
					weekCN,
					date: `${year}-${month}-${day}`,
					dateIOS: `${year}/${month}/${day}`,
					time: `${hour}:${sec}`,
					shortDate: `${month}-${day}`,
					shortDateIOS: `${month}/${day}`,
					festival: ''
				}
				return obj
			},
			// 选择日期
			chooseDay(e) {
				if (!e.weekCN) return

				if (e.timeStamp < this.startRangeTimeStamp) {
					return uni.showToast({
						title: '已过期的时间不可选择哦！',
						icon: 'none'
					})
				}

				// 单选模式逻辑
				if (this.single) {
					this.selectOptionsArray = [e]
					this.confirm()
					return
				}
				// 检查是否不可预订
				const isUnavailable = this.checkDateAvailability(e)
				if (isUnavailable) {
					return uni.showToast({
						title: '该日期不可预订',
						icon: 'none'
					})
				}
				if (this.selectOptionsArray.length === 2) {
					this.selectOptionsArray = []
				}
				if (this.selectOptionsArray.length === 0) {
					this.selectOptionsArray.push(e)
					// 这边必须return 否则首次添加后面会继续执行
					return
				}
				if (this.selectOptionsArray.length === 1) {
					if (e.timeStamp >= this.selectOptionsArray[0].timeStamp) {
						// 如果传入了选择范围，需要判断选择范围
						if (this.handleDayNums(this.selectOptionsArray[0].date, e.date) > this.maxRange) {
							uni.showToast({
								title: `选择范围不能超过${this.maxRange}天`,
								icon: 'none'
							})
						} else {
							// 检查范围内是否有不可预订的日期
							const hasUnavailable = this.checkRangeAvailability(this.selectOptionsArray[0], e)
							if (hasUnavailable) {
								return uni.showToast({
									title: '选择的日期范围内包含不可预订的日期',
									icon: 'none'
								})
								this.selectOptionsArray = []
							} else {
								this.selectOptionsArray.push(e)
							}
						}
					} else {
						this.selectOptionsArray[0] = e
					}
				}
			},

			// 添加检查日期范围内可用性的方法
			checkRangeAvailability(startDate, endDate) {
				if (!this.activeArray || !this.activeArray.length) return false

				// 获取开始和结束时间戳
				const startTime = startDate.timeStamp
				const endTime = endDate.timeStamp

				// 计算天数差
				const dayCount = Math.ceil((endTime - startTime) / (1000 * 60 * 60 * 24)) + 1

				// 检查范围内的每一天
				for (let i = 0; i < dayCount; i++) {
					const currentDate = new Date(startTime + i * 24 * 60 * 60 * 1000)
					const dateObj = this.handleDateData(currentDate)

					// 查找匹配的日期
					const matchedDate = this.activeArray.find(
						(item) => item.date === dateObj.date || item.dateIOS === dateObj.dateIOS
					)

					// 如果找到匹配的日期且price为"不可预订"，则返回true
					if (matchedDate && matchedDate.price === '不可预订') {
						this.selectOptionsArray = []
						return true
					}
				}

				return false
			},
			// 添加检查日期可用性的方法
			checkDateAvailability(dateObj) {
				if (!this.activeArray || !this.activeArray.length) return false

				// 查找匹配的日期
				const matchedDate = this.activeArray.find(
					(item) => item.date === dateObj.date || item.dateIOS === dateObj.dateIOS
				)

				// 如果找到匹配的日期且price为"不可预订"，则返回true
				return matchedDate && matchedDate.price === '不可预订'
			},
			// 处理样式类名
			handleDayClass(item) {
				// 处理节日
				if (this.copyOpts.festival && this.copyOpts.festival.length) {
					let festival
					for (let i in this.copyOpts.festival) {
						if (this.copyOpts.festival[i].date === item.shortDate) {
							item.festival = this.copyOpts.festival[i].title
						}
					}
				}
				if (item.timeStamp < this.startRangeTimeStamp) {
					return 'disable'
				}

				// 单选模式下的选中判断
				if (this.single) {
					if (this.selectOptionsArray.length === 1 && item.timeStamp === this.selectOptionsArray[0].timeStamp) {
						return 'select'
					}
					return ''
				}

				// 范围选择模式下的选中判断
				if (this.selectOptionsArray.length === 1) {
					if (item.timeStamp === this.selectOptionsArray[0].timeStamp) {
						return 'select'
					}
				}

				if (this.selectOptionsArray.length < 2) {
					return
				}

				if (
					item.timeStamp === this.selectOptionsArray[0].timeStamp ||
					item.timeStamp === this.selectOptionsArray[1].timeStamp
				) {
					return 'select'
				}
				if (
					item.timeStamp > this.selectOptionsArray[0].timeStamp &&
					item.timeStamp < this.selectOptionsArray[1].timeStamp
				) {
					return 'area'
				}
			},
			// 处理出可选的开始时间
			handleStartDateArea() {
				let time
				if (this.startDate) {
					time = this.handleDateData(this.startDate).timeStamp
				} else {
					time = this.handleDateData(new Date().toLocaleDateString()).timeStamp
				}
				this.startRangeTimeStamp = time
			},
			// 得出所选天数
			handleDayNums(a, b) {
				const max = this.handleDateData(b)
				const min = this.handleDateData(a)
				const dayNums = Math.floor((max.timeStamp - min.timeStamp) / 86400000)
				return dayNums
			},
			// 判断是不是当天进出
			isTodaySE(item) {
				if (
					this.selectOptionsArray.length === 2 &&
					this.selectOptionsArray[0].timeStamp === item.timeStamp &&
					this.selectOptionsArray[1].timeStamp === item.timeStamp
				) {
					return true
				} else {
					return false
				}
			},
			// 确定选择
			confirm() {
				if (this.single) {
					// 单选模式
					if (this.selectOptionsArray.length === 1) {
						const info = {
							date: this.selectOptionsArray[0],
							dayNums: 1
						}
						this.$emit('confirm', info)
					} else {
						return uni.showToast({
							title: '请选择日期',
							icon: 'none'
						})
					}
				} else {
					// 范围选择模式
					if (this.selectOptionsArray.length === 2) {
						const info = {
							start: this.selectOptionsArray[0],
							end: this.selectOptionsArray[1],
							dayNums: this.dayNums
						}
						this.$emit('confirm', info)
					} else {
						return uni.showToast({
							title: '请选择完整的时间段',
							icon: 'none'
						})
					}
				}
			},
			cancel() {
				this.$emit('cancel')
			}
		}
	}
</script>

<style lang="scss" scoped>
	.calendar {
		box-sizing: border-box;
		display: flex;
		flex-direction: column;
		justify-content: flex-end;
		background-color: #f4f5f9 !important;
	}
	.header {
		position: sticky;
		top: 0;
		z-index: 99;
		background-color: #f4f5f9;
	}
	.header_box {
		padding: 30rpx;
	}
	.weeks {
		background-color: #fff;
		display: flex;
		align-items: center;
		justify-content: space-between;
		border-radius: 24rpx;
		height: 88rpx;
		border-bottom: 1rpx solid #ededed;

		view {
			flex: 1;
			text-align: center;
		}
	}

	.item {
		font-size: 28rpx;
		border-radius: 10rpx;
		.yearMonth {
			text-align: center;
			padding: 50rpx 0;
		}

		.day-wrap {
			display: flex;
			align-items: center;
			flex-wrap: wrap;

			.day {
				width: 14.28%;
				display: flex;
				align-items: center;
				flex-direction: column;
				justify-content: center;
				padding: 6rpx;
				box-sizing: border-box;

				// 禁止选择
				.disable {
					background-color: #ededed;
					border-radius: 10rpx;
				}

				// 日期内部容器样式
				.item {
					width: 100%;
					display: flex;
					align-items: center;
					flex-direction: column;
					justify-content: center;
					box-sizing: border-box;
					padding: 6rpx;
				}
			}

			//选择后的容器样式
			.select {
				background-color: #2367fa;
				border-radius: 10rpx;
				color: #ffffff;
			}
			.activeKey {
				color: #787878;
			}
			.item.select {
				.activeKey {
					color: #ffffff !important;
				}
			}

			// 选择范围区间的样式
			.area {
				background-color: #ffd9b3;
				border-radius: 10rpx;
			}
		}
	}

	.footer {
		height: 60rpx;
		padding: 30rpx;
		display: flex;
		align-items: center;
		padding-bottom: calc(constant(safe-area-inset-bottom));
		padding-bottom: calc(env(safe-area-inset-bottom));
		.inbottom {
			border-top: 1rpx solid #ededed;
			position: fixed;
			width: 750rpx;
			padding: 30rpx;
			box-sizing: border-box;
			bottom: 0;
			left: 0;
			background-color: #f4f5f9;
			width: 100%;
			display: flex;
			align-items: center;
			justify-content: space-between;
			padding-bottom: calc(30rpx + constant(safe-area-inset-bottom));
			padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
			.cancel {
				border: 1rpx solid #ededed;
			}
			button {
				height: 88rpx;
				border-radius: 100rpx;
				line-height: 88rpx;
				letter-spacing: 10rpx;
				width: 300rpx;
				font-weight: bold;
				&::after {
					border: none;
				}
			}
		}
	}
</style>
